home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue54 / Persist / tiPtnFactory.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-01-21  |  12.5 KB  |  381 lines

  1. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2.   TechInsite Pty. Ltd.
  3.   PO Box 429, Abbotsford, Melbourne. 3067 Australia
  4.   Phone: +61 3 9419 6456
  5.   Fax:   +61 3 9419 1682
  6.   Web:   www.techinsite.com.au
  7.   EMail: peter_hinrichsen@techinsite.com.au
  8.  
  9.   Created: 01/12/1999
  10.  
  11.   Notes: Factory pattern.
  12.  
  13. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  14.  
  15.  
  16. {  Useage...
  17.  
  18. interface
  19. uses
  20.   tiPtnFactory
  21.   ;
  22.  
  23. type
  24.  
  25.   TMyClass = class( TObject ) ;
  26.  
  27.   TMyConcrete1 = class( TMyClass ) ;
  28.   TMyConcrete2 = class( TMyClass ) ;
  29.  
  30.   //----------------------------------------------------------------------------
  31.   TMyFactory = class( TtiFactory )
  32.   public
  33.     Function  CreateInstance( const pStrClassID : string ) : TMyClass ;
  34.   end;
  35.  
  36. function gMyFactory : TMyFactory ;
  37.  
  38. implementation
  39. var
  40.   uMyFactory : TMyFactory ;
  41.  
  42. function gMyFactory : TMyFactory ;
  43. begin
  44.   if uMyFactory = nil then
  45.     uMyFactory := TMyFactory.Create ;
  46.   result := uMyFactory ;
  47. end ;
  48.  
  49. //----------------------------------------------------------------------------
  50. function TMyFactory.CreateInstance( const pStrClassID: string): TMyClass ;
  51. begin
  52.   result := TMyClass( DoCreateInstance( pStrClassID )) ;
  53. end;
  54.  
  55. implementation
  56.   gMyFactory.RegisterClass( 'Name1', TMyConcrete1 ) ;
  57.   gMyFactory.RegisterClass( 'Name2', TMyConcrete2 ) ;
  58.  
  59. finalization
  60.   uMyFactory.Free ;
  61.  
  62. end.
  63.  
  64. }
  65.  
  66. unit tiPtnFactory ;
  67.  
  68. interface
  69. uses
  70.    Classes             // For TObject
  71.   ;
  72.  
  73. type
  74.  
  75.   // Class reference for TObject descendants
  76.   //----------------------------------------------------------------------------
  77.   TObjectClassRef = class of TObject ;
  78.  
  79.   // Class reference for TComponent descendants
  80.   //----------------------------------------------------------------------------
  81.   TComponentClassRef = class of TComponent ;
  82.  
  83.   // Tell the factory to create a TObject or a TComponent.
  84.   // This is necessary as TComponent descendents will need
  85.   // an owner parameter.
  86.   //----------------------------------------------------------------------------
  87.   TCreateAs = ( caTObject, caTComponent ) ;
  88.  
  89.   // After a class is registered with the factory, a
  90.   // TClassMappingAbstract descendant will be added to the
  91.   // list of registered objects.
  92.   //----------------------------------------------------------------------------
  93.   TClassMappingAbstract = class( TObject )
  94.   private
  95.     FStrClassID : string ;      // A string to identify the class
  96.     FCreateAs   : TCreateAs ;   // Create as a TObject or TComponent
  97.     FBoolSingleton : boolean ;  // Cache this instance ?
  98.   public
  99.     property    ClassID  : string read  FStrClassID
  100.                                   write FStrClassID ;
  101.     property    CreateAs : TCreateAs read  FCreateAs
  102.                                      write FCreateAs ;
  103.     property    Singleton : boolean read FBoolSingleton
  104.                                     write FBoolSingleton ;
  105.   end ;
  106.  
  107.   // Used when a TObject descendant is registered
  108.   //----------------------------------------------------------------------------
  109.   TClassMappingObject = class( TClassMappingAbstract )
  110.   private
  111.     FClassRef     : TObjectClassRef ;  // TObject class reference
  112.   public
  113.     Constructor CreateExt( const pStrClassID : string ;
  114.                   pClassRef : TObjectClassRef ;
  115.                   const pBoolSingleton : boolean ) ;
  116.     property    ClassRef : TObjectClassRef read FClassRef
  117.                                            write FClassRef ;
  118.   end ;
  119.  
  120.   // Used when a TComponent descendant is registered
  121.   //----------------------------------------------------------------------------
  122.   TClassMappingComponent = class( TClassMappingAbstract )
  123.   private
  124.     FClassRef     : TComponentClassRef ;  // TComponent class reference
  125.   public
  126.     Constructor CreateExt( const pStrClassID : string ;
  127.                   pClassRef : TComponentClassRef ;
  128.                   const pBoolSingleton : boolean ) ;
  129.     property    ClassRef : TComponentClassRef read FClassRef
  130.                                            write FClassRef ;
  131.   end ;
  132.  
  133.   { ToDo 1 -cPatterns: Move the cache part of the factory to a flyweight pattern}
  134.   // The abstract factory
  135.   //----------------------------------------------------------------------------
  136.   TtiFactory = class( TObject )
  137.   private
  138.     FClassMappings : TStringList ;  // List of registered classes
  139.     FObjectCache   : TStringList ;  // Cache of already created objects
  140.   protected
  141.     // Create an instance of our class, or return a pointer to the existing
  142.     // instance if already created. This function is protected to force
  143.     // you to create a public implementation in a concrete class.
  144.     Function   DoCreateInstance( const pStrClassID : string ; owner : TComponent = nil ) : TObject ; virtual ;
  145.   public
  146.     Constructor Create ; virtual ;
  147.     Destructor  Destroy ; override ;
  148.     // Register a TObject descendant
  149.     Procedure   RegisterClass( const pStrClassID : string;
  150.                               pClassRef : TObjectClassRef ;
  151.                               const pBoolSingleton : boolean = false ) ; overload ;
  152.     // Register a TComponent descendant
  153.     Procedure   RegisterClass( const pStrClassID : string;
  154.                               pClassRef : TComponentClassRef ;
  155.                               const pBoolSingleton : boolean = false ) ; overload ;
  156.     Property    ClassMappings : TStringList read FClassMappings ;
  157.  
  158.   end ;
  159.  
  160. implementation
  161. uses
  162.   SysUtils   // UpperCase
  163.   ,Dialogs   // MessageDlg
  164.   ;
  165.  
  166. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  167. // *
  168. // * TClassMappingObject: Hold information about how to create a TObject
  169. // *                      descendant.
  170. // *
  171. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  172. constructor TClassMappingObject.CreateExt(
  173.                   const pStrClassID : string;
  174.                   pClassRef: TObjectClassRef ;
  175.                   const pBoolSingleton : boolean ) ;
  176. begin
  177.   Create ;
  178.   ClassID  := pStrClassID ;
  179.   ClassRef := pClassRef  ;
  180.   CreateAs := caTObject ;
  181.   Singleton := pBoolSingleton ;
  182. end;
  183.  
  184. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  185. // *
  186. // * TClassMappingComponent: Hold information about how to create a
  187. // *                         TComponent descendant.
  188. // *
  189. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  190. constructor TClassMappingComponent.CreateExt(
  191.                   const pStrClassID: string;
  192.                   pClassRef: TComponentClassRef ;
  193.                   const pBoolSingleton : boolean ) ;
  194. begin
  195.   Create ;
  196.   ClassID  := pStrClassID ;
  197.   ClassRef := pClassRef  ;
  198.   CreateAs := caTComponent ;
  199.   Singleton := pBoolSingleton ;
  200. end;
  201.  
  202. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  203. // *
  204. // * TFactoryAbstract: The abstract factory.
  205. // *                   Used to create TObject and TComponent descendants.
  206. // *
  207. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  208. constructor TtiFactory.Create;
  209. begin
  210.   inherited ;
  211.   FClassMappings := TStringList.Create ;
  212.   FObjectCache   := TStringList.Create ;
  213. end;
  214.  
  215. //------------------------------------------------------------------------------
  216. destructor TtiFactory.Destroy;
  217. var i : integer ;
  218. begin
  219.  
  220.   // Scan through FClassMappings,
  221.   // and free any associated objects
  222.   for i := 0 to FClassMappings.Count - 1 do
  223.     TObject( FClassMappings.Objects[i] ).Free ;
  224.   // Free FClassMappings
  225.   FClassMappings.Free ;
  226.  
  227.   // Free any objects in the cache
  228.   for i := 0 to FObjectCache.Count - 1 do begin
  229.     try
  230.       TObject( FObjectCache.Objects[i] ).Free ;
  231.     except end ;
  232.   end ;
  233.   FObjectCache.Free ;
  234.  
  235.   // Call inherited
  236.   inherited ;
  237.  
  238. end;
  239.  
  240. // Register a class mapping for a TObject descendant.
  241. //------------------------------------------------------------------------------
  242. procedure TtiFactory.RegisterClass(
  243.                                const pStrClassID: string;
  244.                                pClassRef: TObjectClassRef ;
  245.                                const pBoolSingleton : boolean );
  246. var i : integer ;
  247.     lClassMapping : TClassMappingObject ;
  248.     lStrClassID : string ;
  249. begin
  250.   lStrClassID := upperCase( pStrClassID ) ;
  251.  
  252.   // Does the class mapping alread exist?
  253.   i := FClassMappings.IndexOf( lStrClassID );
  254.  
  255.   // If yes, report an error.
  256.   // We do not raise an exception here as we may be inside an
  257.   // initialization section.
  258.   if i <> -1 then begin
  259.     messageDlg( 'Registering a duplicate ' +
  260.                 'class mapping <' +
  261.                 pStrClassID + '>',
  262.                 mtInformation,
  263.                 [mbOK],
  264.                 0 ) ;
  265.     Exit ; //==>
  266.   end ;
  267.  
  268.   // Create the class mapping object
  269.   lClassMapping := TClassMappingObject.CreateExt(
  270.                       lStrClassID,
  271.                       pClassRef,
  272.                       pBoolSingleton ) ;
  273.  
  274.   // Add the class mapping object to the list
  275.   FClassMappings.AddObject( upperCase( pStrClassID ),
  276.                              lClassMapping ) ;
  277.  
  278. end;
  279.  
  280. // Register a class mapping for a TComponent descendant.
  281. //------------------------------------------------------------------------------
  282. procedure TtiFactory.RegisterClass(
  283.                                   const pStrClassID: string;
  284.                              pClassRef: TComponentClassRef ;
  285.                              const pBoolSingleton : boolean = false );
  286. var i : integer ;
  287.     lClassMapping : TClassMappingComponent ;
  288.     lStrClassID : string ;
  289. begin
  290.   lStrClassID := upperCase( pStrClassID ) ;
  291.  
  292.   // Does the class mapping already exist?
  293.   i := FClassMappings.IndexOf( lStrClassID );
  294.  
  295.   // If yes, report an error.
  296.   // We do not raise an exception here as we may be inside an
  297.   // initialization section.
  298.   if i <> -1 then begin
  299.     messageDlg( 'Registering a duplicate ' +
  300.                 'class mapping <' +
  301.                 pStrClassID + '>',
  302.                 mtInformation,
  303.                 [mbOK],
  304.                 0 ) ;
  305.     Exit ; //==>
  306.   end ;
  307.  
  308.   // Create a classMapping object
  309.   lClassMapping := TClassMappingComponent.CreateExt(
  310.                       lStrClassID,
  311.                       pClassRef,
  312.                       pBoolSingleton ) ;
  313.  
  314.   // Add the ClassName, and ClassMapping object to the list
  315.   FClassMappings.AddObject( upperCase( pStrClassID ),
  316.                              lClassMapping ) ;
  317.  
  318. end;
  319.  
  320. // Either look up an existing instance of the object in the cache, or
  321. // create a new one. DoCreateInstance should only be called from a concrete
  322. // descendant of TFactoryAbstract.
  323. //------------------------------------------------------------------------------
  324. function TtiFactory.DoCreateInstance( const pStrClassID : string ; owner : TComponent = nil ) : TObject ;
  325. var lIntCacheIndex   : integer ;
  326.     lIntMappingIndex : integer ;
  327.     lStrClassID      : string ;
  328.     lClassMapping    : TClassMappingAbstract ;
  329. begin
  330.  
  331.   // Get a temporary copy of ClassID, in upper case
  332.   lStrClassID := upperCase( pStrClassID ) ;
  333.  
  334.   // Does the class mapping exist?
  335.   lIntMappingIndex := FClassMappings.IndexOf( lStrClassID );
  336.  
  337.   // If not, then raise an exception
  338.   // We can raise an exception here as we are not likely to be inside
  339.   // initialization code
  340.   if lIntMappingIndex = -1 then
  341.     Raise Exception.Create( 'Request for invalid class ' +
  342.                             'name <' +
  343.                             pStrClassID + '>' +
  344.                             ' Called by factory: ' +
  345.                             ClassName ) ;
  346.  
  347.   // Is the object already in the cache?
  348.   // Yes, then return the cahced copy
  349.   // No, then create one
  350.   lIntCacheIndex := FObjectCache.IndexOf( lStrClassID );
  351.  
  352.   // The object is not already in the cache
  353.   if lIntCacheIndex = -1 then begin
  354.     // Get a pointer to the correct class mapping
  355.     lClassMapping := TClassMappingAbstract(
  356.                 FClassMappings.Objects[lIntMappingIndex] ) ;
  357.  
  358.     // Do we create this object as a TComponent or a TObject?
  359.     if lClassMapping.CreateAs = caTComponent then
  360.       result :=
  361.         TClassMappingComponent( lClassMapping ).ClassRef.Create( owner )
  362.     else
  363.       result :=
  364.         TClassMappingObject( lClassMapping ).ClassRef.Create ;
  365.  
  366.     // If this class is to be cached, then add it to the list
  367.     if lClassMapping.Singleton then
  368.       FObjectCache.AddObject( lStrClassID, result ) ;
  369.  
  370.   // The object is already in the cache
  371.   end else begin
  372.     // So return the existing copy
  373.     result := FObjectCache.Objects[ lIntCacheIndex ] ;
  374.  
  375.   end ;
  376.  
  377. end ;
  378.  
  379. end.
  380.  
  381.